﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace DBCUnit.ConsoleApp
{

	public class Program
	{
		static void Main(string[] args)
		{

			foreach(string arg in args)
			{
				Assembly contractAssembly = GetContractAssembly(arg);
				if (contractAssembly != null)
					ExecuteContracts(contractAssembly);
			}

		}

		private static Assembly GetContractAssembly(string assemblyFile)
		{
			Assembly asm;
			try
			{
				assemblyFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyFile);
				asm = Assembly.LoadFile(assemblyFile);
			}
			catch
			{
				asm = null;
				Console.WriteLine("Assembly Not Found: " + assemblyFile);
			}
			return asm;
		}

		private static void ExecuteContracts(Assembly contractAssembly)
		{
			foreach(Type type in contractAssembly.GetTypes())
			{
				bool isContract = CheckForContract(type);
				if (isContract)
				{
					ExecuteContract(type);
				}
			}
		}

		private static bool CheckForContract(ICustomAttributeProvider type)
		{
			bool isContract = false;

			object[] attributes = type.GetCustomAttributes(typeof (ConditionAttribute), true);
			if (attributes != null && attributes.Length > 0)
				isContract = true;

			return isContract;
		}

		private static void ExecuteContract(Type type)
		{
			object contractObject = GetContractObject(type);

			if (contractObject == null)
				return;

			MethodInfo PreConditionMethod = null;
			IList<MethodInfo> PostConditionMethods = new List<MethodInfo>();

			foreach(MethodInfo methodInfo in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))
			{
				if (IsPreConditionMethod(methodInfo))
				{
					if (PreConditionMethod == null)
						PreConditionMethod = methodInfo;
					else
						Console.WriteLine("PreCondition Method Found More Than Once.");
				}
				if (IsPostConditionMethod(methodInfo))
				{
					PostConditionMethods.Add(methodInfo);
				}
			}

			if (PreConditionMethod != null)
				PreConditionMethod.Invoke(contractObject, null);

			foreach(MethodInfo postConditionMethod in PostConditionMethods)
				if (postConditionMethod != null)
				{
					try
					{
						AssertionContext.Setup();
						postConditionMethod.Invoke(contractObject, null);
						AssertionContext.VerifyAll();
					}
					catch(Exception ex)
					{
						Console.WriteLine(ex.ToString());
					}
				}
		}

		private static object GetContractObject(Type type)
		{
			object contractType = null;
			try
			{
				contractType = Activator.CreateInstance(type);
			}
			catch(Exception ex)
			{
				Console.WriteLine("Contract Type cannot instantiate: " + ex);
			}
			return contractType;
		}

		private static bool IsPreConditionMethod(ICustomAttributeProvider methodInfo)
		{
			bool isPreConditionMethod = false;

			object[] attributes = methodInfo.GetCustomAttributes(typeof (PreConditionAttribute), true);
			if (attributes != null && attributes.Length > 0)
				isPreConditionMethod = true;

			return isPreConditionMethod;
		}

		private static bool IsPostConditionMethod(ICustomAttributeProvider methodInfo)
		{
			bool isPostConditionMethod = false;

			object[] attributes = methodInfo.GetCustomAttributes(typeof(PostConditionAttribute), true);
			if (attributes != null && attributes.Length > 0)
				isPostConditionMethod = true;

			return isPostConditionMethod;
		}

	}

}
